package main

import (
	"context"
	"fmt"
	"log"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Student struct {
	Name string `bson:"name"`
	Age  int    `bson:"age"`
}

func main() {
	// 设置客户端连接配置, mongodb://用户名:密码@host:port/数据库
	clientOptions := options.Client().ApplyURI("mongodb://root:root123@localhost:27017/admin")

	// 连接到MongoDB
	client, err := mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		log.Fatal(err)
	}

	// 检查连接
	err = client.Ping(context.TODO(), nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to MongoDB!")

	// 指定获取要操作的数据集
	collection := client.Database("test").Collection("c1")

	// 插入文档
	insert(collection)

	// 修改文档
	update(collection)

	// 查找文档
	read(collection)

	// 删除文档
	delete(collection)

	// 断开连接
	err = client.Disconnect(context.TODO())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connection to MongoDB closed.")
}

func insert(collection *mongo.Collection) {

	s1 := Student{"欧阳修", 12}
	s2 := Student{"辛弃疾", 10}
	s3 := Student{"白居易", 11}

	// 插入一条数据
	insertResult, err := collection.InsertOne(context.TODO(), s1)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Inserted a single document: ", insertResult.InsertedID)

	// 插入多条数据
	students := []interface{}{s2, s3}
	insertManyResult, err := collection.InsertMany(context.TODO(), students)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)
}

func update(collection *mongo.Collection) {

	// 将李白的年龄增加1
	filter := bson.M{"name": "李白"}
	update := bson.M{
		"$inc": bson.M{
			"age": 1,
		},
	}

	updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)
}

func read(collection *mongo.Collection) {

	// 查找李白的文档
	filter := bson.M{"name": "李白"}
	// 创建一个Student变量用来接收查询的结果
	var result Student
	err := collection.FindOne(context.TODO(), filter).Decode(&result)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Found a single document: %+v\n", result)

	// 查询多个
	// 将选项传递给Find()
	findOptions := options.Find()
	findOptions.SetLimit(2)

	// 定义一个切片用来存储查询结果
	var results []*Student

	// 把bson.D{{}}作为一个filter来匹配所有文档
	cur, err := collection.Find(context.TODO(), bson.D{{}}, findOptions)
	if err != nil {
		log.Fatal(err)
	}

	// 查找多个文档返回一个光标
	// 遍历游标允许我们一次解码一个文档
	for cur.Next(context.TODO()) {
		// 创建一个值，将单个文档解码为该值
		var elem Student
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}
		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// 完成后关闭游标
	cur.Close(context.TODO())
	fmt.Printf("Found multiple documents (array of pointers): %#v\n", results)
}

func delete(collection *mongo.Collection) {
	// 删除名字是杜甫的文档
	deleteResult1, err := collection.DeleteOne(context.TODO(), bson.M{"name": "杜甫"})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Deleted %v documents in the trainers collection\n", deleteResult1.DeletedCount)

	// 删除所有
	// deleteResult2, err := collection.DeleteMany(context.TODO(), bson.D{{}})
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// fmt.Printf("Deleted %v documents in the trainers collection\n", deleteResult2.DeletedCount)
}
